using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;


namespace Perovich.GameObjects.ThreeDimensional
{
    /// <summary>
    /// The <see cref="Camera"/> class represents a viewport and projection in 3D space.
    /// </summary>
    public class Camera : DrawableObject3D
    {
        /// <summary>
        /// Gets the view matrix defined by this <see cref="Camera"/>.
        /// </summary>
        public Matrix ViewMatrix { get; private set; }

        /// <summary>
        /// Gets the projection matrix defined by this <see cref="Camera"/>.
        /// </summary>
        public Matrix ProjectionMatrix { get; private set; }

        private Vector3 _target;
        private float _fov;
        private float _aspectRatio;
        private float _nearClip;
        private float _farClip;
        
        /// <summary>
        /// Gets or sets the far clip for this <see cref="Camera"/>.
        /// </summary>
        public float FarClip
        {
            get { return _farClip; }
            set { _farClip = value; }
        }

        /// <summary>
        /// Gets or sets the near clip for this <see cref="Camera"/>.
        /// </summary>
        public float NearClip
        {
            get { return _nearClip; }
            set { _nearClip = value; }
        }

        /// <summary>
        /// Gets or sets the aspect ratio for this <see cref="Camera"/>.
        /// </summary>
        public float AspectRatio
        {
            get { return _aspectRatio; }
            set { _aspectRatio = value; }
        }

        /// <summary>
        /// Gets or sets the field of view for this <see cref="Camera"/>.
        /// </summary>
        public float FieldOfView
        {
            get { return _fov; }
            set { _fov = value; }
        }

        /// <summary>
        /// Gets or sets the position this <see cref="Camera"/> is looking at.
        /// </summary>
        public Vector3 Target
        {
            get { return _target; }
            set
            {
                _target = value;
                UpdateMatrixes();
            }
        }

        /// <summary>
        /// Constructs a new instance of the <see cref="Camera"/> class.
        /// </summary>
        /// <param name="game"><see cref="Perovich.GameObjects.Interfaces.ICanHoldComponents"/> instance that this <see cref="DrawableComponent"/> will be constructed for.</param>
        public Camera(Interfaces.ICanHoldComponents game)
            : base(game)
        {
            FieldOfView = 1;
            NearClip = 1;
            FarClip = 100;
            PositionChanged += new EventHandler(Camera_PositionChanged);
        }

        void Camera_PositionChanged(object sender, EventArgs e)
        {
            UpdateMatrixes();
        }


        /// <summary>
        /// Allows the <see cref="Camera"/> to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            UpdateMatrixes();
            base.Update(gameTime);
        }

        void UpdateMatrixes()
        {
            ViewMatrix = Matrix.CreateLookAt(Position, _target, Vector3.Up);
            ProjectionMatrix = Matrix.CreatePerspectiveFieldOfView(_fov, _aspectRatio, _nearClip, _farClip);
        }
    }
}
